using System;
using System.Diagnostics;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Xfinity.AI;
using Xfinity.Entities.Managers;
using Xfinity.Console;
using Xfinity.Engine; 
using Xfinity.Engine.Plugins;
using Xfinity.Physics;
using Xfinity.Actors;
using Xfinity.Scripting;
using Xfinity.Weapons;
using Xfinity.Debugging;
using Xfinity.Rendering;
using System.Collections.Generic;
using Xfinity.Levels;
using Xfinity.Engine.GameStates;
using Xfinity.Audio;
using System.Windows.Forms;
using System.Security.Policy;
using System.Runtime.Serialization.Formatters.Binary;
using Xfinity.Cameras;
using Xfinity.Maths.Geometry;
using Xfinity.Settings;
using Xfinity.Materials;
using System.Reflection;
using Xfinity.Rendering.Renderers.Deferred;

namespace Xfinity
{
    /// <summary>
    /// The main Xfinity class
    /// </summary>
    public class GameManager : GlobalManager, IPauseHolder
    {        
        class CameraHolder : ICameraHolder
        {

            #region ICameraHolder Members

            #region ActiveCamera
            private ICamera cam;

            /// <summary>
            /// ActiveCamera.
            /// </summary>
            public ICamera ActiveCamera
            {
                [DebuggerStepThroughAttribute]
                get { return cam; }
                [DebuggerStepThroughAttribute]
                set { cam = value; }
            }
            #endregion


            #endregion
        }
        class TriangleListUpdater : IGameComponent, IUpdateable
        {
            #region IGameComponent Members

            public void Initialize()
            {

            }

            #endregion

            #region IUpdateable Members

            public bool Enabled
            {
                get { return true; }
            }

            public event EventHandler EnabledChanged;

            public void Update(GameTime gameTime)
            {
                foreach (IWorldViewProjection list in TriangleListsContainer.Lists)
                {
                    list.World = Matrix.Identity;
                    list.View = GraphicsHelper.ActiveCamera.View;
                    list.Projection = GraphicsHelper.ActiveCamera.Projection;
                }

            }

            public int UpdateOrder
            {
                get { return 0; }
            }

            public event EventHandler UpdateOrderChanged;

            #endregion
        }

        

        /// <summary>
        /// Constructs a new GameManager.
        /// </summary>
        /// <param name="game">The Game being used.</param>
        public GameManager(Game game)
            : base("Game Manager", false, game)
        {

            if (game == null)
            {
                throw new ArgumentNullException("game");
            }
            
            Trace.Listeners.Add(new Xfinity.Console.Console.ConsoleTraceListener());
            GameManager.game = game;
            level = new Level(game);
            game.Components.Add(level);
            playerManager = new PlayerManager("", game);
            aiManager = new AIManager(playerManager.PlayerName + Xfinity.Resources.Resources.AIManager, game);
            content = new ContentManager(game.Services);
            camera = new Camera(game);
            game.Components.Add(camera);
            GCBeep beep;
            CameraHolder h = new CameraHolder();
            h.ActiveCamera = camera;
            game.Services.AddService(typeof(ICameraHolder), h);
            GraphicsHelper.ActiveCamera = camera;
            weaponManager = new WeaponManager(playerManager.PlayerName + " Weapon Manager", game);
            new PhysicsManager(game);
            scriptManager = new ScriptManager("Script manager", game);
            AudioHelper.InitialiseAudio(game, playerManager.Player);
            currentState = new MenuGameState();
            new InputHandler(game);
            StandardConsole.DefaultConsole = new Xfinity.Console.Console(game);
            BloomComponent bloom = new BloomComponent(game);
            game.Components.Add(bloom);
            Visible = true;
            MaterialType type = MaterialType.Metal;
            Debug.Assert(true, type.ToString());
            //InputHandler.BindToCommand(EngineSettings.ConsoleKey, "ToggleConsole");
            game.Components.Add(new TriangleListUpdater());
            skybox = new Skybox("Content/textures/DebuggingCubemap", camera, game);
            game.Components.Add(skybox);
            DeferredRenderer r = new DeferredRenderer(Game);
            game.Components.Add(r);
            flare = new LensFlare(Vector3.One, game);
            //game.Components.Add(flare);

        }
        static Skybox skybox;
        static LensFlare flare;
        private static GameTime currentGameTime = new GameTime();

        /// <summary>
        /// 
        /// </summary>
        public static GameTime CurrentGameTime
        {
            get { return currentGameTime; }
            set { currentGameTime = value; }
        }

        private static ContentManager content;

        /// <summary>
        /// Global content manager.
        /// </summary>
        public static ContentManager Content
        {
            get { return content; }
        }


        private static Level level;

        /// <summary>
        /// 
        /// </summary>
        public static Level Level
        {
            get { return level; }
            set { level = value; }
        }


        private static bool debugging = false;

        /// <summary>
        /// 
        /// </summary>
        public static bool Debugging
        {
            get { return debugging; }
            set { debugging = value; }
        }
        public override void Draw(GameTime gameTime)
        {
            //Level.Draw(gameTime);
            base.Draw(gameTime);
        }

        private static bool paused;

        /// <summary>
        /// Gets or sets a value indicating whether the game is paused.
        /// </summary>
        /// <value><c>true</c> if paused; otherwise, <c>false</c>.</value>
        public static bool Paused
        {
            get { return paused; }
            set { paused = value; }
        }


        private static ScriptManager scriptManager;

        /// <summary>
        /// Gets the script manager.
        /// </summary>
        /// <value>The script manager.</value>
        public static ScriptManager ScriptManager
        {
            get { return scriptManager; }
        }

        private static List<IUpdateable> engineManagedComponents = new List<IUpdateable>();

        /// <summary>
        /// 
        /// </summary>
        public static List<IUpdateable> EngineManagedComponents
        {
            get { return engineManagedComponents; }
            set { engineManagedComponents = value; }
        }

        private static Game game;

        /// <summary>
        /// Gets and sets the Game associated with this GameComponent.
        /// </summary>
        /// <value></value>
        /// <returns>
        /// The Game associated with this GameComponent.
        /// </returns>
        public static new Game Game
        {
            [DebuggerStepThrough]
            get { return game; }
            [DebuggerStepThrough]
            set { game = value; }
        }
        private static Camera camera;

        /// <summary>
        /// Gets or sets the camera.
        /// </summary>
        /// <value>The camera.</value>
        public static Camera Camera
        {
            [DebuggerStepThrough]
            get { return camera; }
            [DebuggerStepThrough]
            set { camera = value; }
        }

        /// <summary>
        /// Initialises the global manager.
        /// </summary>
        public override void Initialize()
        {
            AddUnhandledExceptionHandler();
            IGraphicsDeviceService gds = (IGraphicsDeviceService)(game.Services.GetService(typeof(IGraphicsDeviceService)));
            GraphicsHelper.GraphicsDevice = gds.GraphicsDevice;
            GraphicsHelper.SpriteBatch = new SpriteBatch(GraphicsHelper.GraphicsDevice);
            GraphicsHelper.DefaultFont = content.Load<SpriteFont>(@"Content\Fonts\DefaultFont");
            base.Initialize();
            Enabled = true;
            ScriptManager.Initialize();
            CommandParser.ScanForBoundCommands();
            Xfinity.Systems.StartupInitialisation.RunStartupMethods();
        }

        private static void AddUnhandledExceptionHandler()
        {
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
        }

        private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
        {
            Exception ex = (Exception)args.ExceptionObject;
            Type t = ex.GetType();
            if (t == typeof(TypeLoadException))
            {
                TypeLoadException e = (TypeLoadException)ex;
                if (!Debugger.IsAttached && e.TypeName.Contains("Microsoft.Xna"))
                {
                    MessageBox.Show("You need the latest version of the XNA Framework.");
                }
            }

            else
            {
                if (!Debugger.IsAttached)
                {
                    MessageBox.Show(ex.ToString(), "Unhandled exception occured.");
                }
            }
            using (EventLog log = new EventLog("Xfinity", Environment.MachineName, "Xfinity engine"))
            {
                if (!EventLog.SourceExists("Xfinity Engine"))
                {
                    EventSourceCreationData data = new EventSourceCreationData("Xfinity Engine", "Xfinity");
                    data.MachineName = ".";
                    EventLog.CreateEventSource(data);
                }
                EventInstance instance = new EventInstance(log.Entries.Count, 0, EventLogEntryType.Error);
                log.WriteEvent(instance, ex);
            }
        }
        public override void Update(GameTime gameTime)
        {
            currentGameTime = gameTime;
            foreach (ParticleSystems.ParticleSystem system in ParticleSystems.ParticleSystem.ActiveSystems)
            {
                system.SetCamera(GraphicsHelper.ActiveCamera.View, GraphicsHelper.ActiveCamera.Projection);
            }
            base.Update(gameTime);
        }


        private static PlayerManager playerManager;

        /// <summary>
        /// Gets the player manager.
        /// </summary>
        /// <value>The player manager.</value>
        public static PlayerManager PlayerManager
        {
            [DebuggerStepThrough]
            get { return playerManager; }
        }

        private static AIManager aiManager;

        /// <summary>
        /// Gets the AI manager.
        /// </summary>
        /// <value>The AI manager.</value>
        public static AIManager AIManager
        {
            [DebuggerStepThrough]
            get { return aiManager; }
        }
        private static WeaponManager weaponManager;

        /// <summary>
        /// Gets the weapon manager.
        /// </summary>
        /// <value>The weapon manager.</value>
        public static WeaponManager WeaponManager
        {
            [DebuggerStepThrough]
            get { return weaponManager; }
        }
        private static GameState currentState;

        /// <summary>
        /// 
        /// </summary>
        public static GameState CurrentState
        {
            get { return currentState; }
            set { currentState = value; }
        }

        protected override void LoadGraphicsContent(bool loadAllContent)
        {
            
            base.LoadGraphicsContent(loadAllContent);
        }
        /// <summary>
        /// Loads the player character.
        /// </summary>
        public static void LoadPlayerCharacter()
        {
            //playerManager.Player.Model = content.Load<Model>(@"Content\Models\Player\PlayerModel");
        }
        private Random random = new Random();

        /// <summary>
        /// Gets the random number generator.
        /// </summary>
        /// <value>The random.</value>
        public Random Random
        {
            [DebuggerStepThrough]
            get { return random; }
        }

        /// <summary>
        /// Creates an NPC character.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="characterName">Name of the character.</param>
        public void CreateNpcCharacter(string model, string characterName)
        {
            Agent agent = new Agent(characterName, "Generic", Game);
            agent.Health = 100;
            string name = Path.GetFileName(model);
            if (name == "{Random}")
            {
                string[] filenames = Directory.GetFiles(Path.GetDirectoryName(model));
                model = filenames[random.Next(filenames.Length)];
            }

            agent.Model = content.Load<Model>(Path.GetDirectoryName(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),model))) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(model));
            agent.Visible = true;
            agent.Enabled = true;
            agent.LoadGraphics(true);
            aiManager.Agents.Add(agent);
        }



        #region IPauseHolder Members

        bool IPauseHolder.Paused
        {
            get
            {
                return paused;
            }
            set
            {
                paused = value;
            }
        }

        #endregion
    }
}